{This file is part of CheLive.

CheLive is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Foobar is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar.  If not, see <http://www.gnu.org/licenses/>.}

{Реализует работу с моделями}
unit CoreMesh;

interface

uses
	SysUtils, gl, glext, CoreTypes, CoreMath, CoreVBO;

type
	TMesh = class(TListItem)
		vbo: TVBO;

		function Render: Integer; virtual; abstract;
		procedure UseVBO; virtual; abstract;
	end;

	TMeshVert = record
		coord: TVec2;
		norm, vert: TVec3;
	end;

	TMeshFace = array[0..2] of Word;

	TStaticMesh = class(TMesh)
		fileName: String;
		numVerts, numFaces: Integer;
		verts: array of TMeshVert;
		faces: array of TMeshFace;

		function Load(fileName_: String): Boolean;
		function Render: Integer; override;
		procedure RenderNorms(len: Single; c: TVec4; faceNorms: Boolean = False);
		procedure UseVBO; override;
	end;

implementation

uses
	CoreConsole;

function TStaticMesh.Load(fileName_: String): Boolean;
var
	f: File;
begin
	Result := False;

	{$I-}
	AssignFile(f, fileName_);
	Reset(f, 1);
	{$I+}

	if IOResult <> 0 then
	begin
		con.Msg('#Не удается открыть файл модели "%s"', [fileName_]);
		Exit;
	end;

	BlockRead(f, numVerts, SizeOf(numVerts));
	BlockRead(f, numFaces, SizeOf(numFaces));

	SetLength(verts, numVerts);
	SetLength(faces, numFaces);

	BlockRead(f, verts[0], numVerts * SizeOf(TMeshVert));
	BlockRead(f, faces[0], numFaces * SizeOf(TMeshFace));

	CloseFile(f);
	
	fileName := fileName_;

	con.Msg('Модель "%s" загружена успешно', [fileName]);

	Result := True;
end;

function TStaticMesh.Render: Integer;
begin
	if fileName = '' then
	begin
		Result := 0;
		Exit;
	end;
	
	Result := numFaces;

	if vbo <> nil then
	begin
		vbo.Render(VBO_TRIS, 0, numFaces * 3);
	end else
	begin
		glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
		glClientActiveTextureARB(GL_TEXTURE0_ARB);
		
		glInterleavedArrays(GL_T2F_N3F_V3F, SizeOf(TMeshVert), @verts[0]);
		glDrawElements(GL_TRIANGLES, numFaces * 3, GL_UNSIGNED_SHORT, @faces[0]);
		
		glPopClientAttrib;
	end;
end;

procedure TStaticMesh.RenderNorms(len: Single; c: TVec4; faceNorms: Boolean = False);
var
	i, j: Integer;
	v, v2, v3, n: TVec3;
begin
	if fileName = '' then
	begin
		Exit;
	end;
	
	glDisable(GL_LIGHTING);
	glColor4f(c.x, c.y, c.z, c.w);
	glBegin(GL_LINES);

	for i := 0 to numFaces - 1 do
	begin
		if faceNorms then
		begin
			//calc normal
			v := verts[faces[i][0]].vert;
			v2 := verts[faces[i][1]].vert;
			v3 := verts[faces[i][2]].vert;
			v2 := v2 - v;
			v3 := v3 - v;
			v := v2 * v3;
			n := Vec3Norm(v) * len;
			
			//render normal
			v := verts[faces[i][0]].vert;
			v2 := verts[faces[i][1]].vert;
			v3 := verts[faces[i][2]].vert;
			v := (v + v2 + v3) / 3;
			glVertex3fv(@v);
			v := v + n;
			glVertex3fv(@v);
		end else
		begin
			for j := 0 to 2 do
			begin
				v := verts[faces[i][j]].vert;
				glVertex3fv(@v);
				n := verts[faces[i][j]].norm * len;
				v := v + n;
				glVertex3fv(@v);
			end;
		end;
	end;

	glEnd;
end;

procedure TStaticMesh.UseVBO;
begin
	if vbo <> nil then
	begin
		vbo.Free;
	end;

	vbo := TVBO.Create;
	vbo.Init(VBO_DRAW or VBO_STATIC, VBO_COORD or VBO_NORM or VBO_VERT, VBO_WORD, numVerts, @verts[0], numFaces * 3, @faces[0]);
end;

end.
